package timing.ukulele.user.controller;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Import;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import timing.ukulele.common.data.ResponseData;
import timing.ukulele.common.dictionary.ThirdPartyTypeEnum;
import timing.ukulele.data.portal.view.SysTenantVO;
import timing.ukulele.data.user.view.PasswordVO;
import timing.ukulele.data.user.view.UserVO;
import timing.ukulele.facade.portal.feign.SysTenantFeignFacade;
import timing.ukulele.facade.user.mvc.UserControllerFacade;
import timing.ukulele.redisson.cache.CacheManager;
import timing.ukulele.share.Constant;
import timing.ukulele.user.config.properties.BindEmailProperties;
import timing.ukulele.user.model.MailBean;
import timing.ukulele.user.persistent.SysThirdPartyUser;
import timing.ukulele.user.persistent.SysUser;
import timing.ukulele.user.service.SysThirdPartyUserService;
import timing.ukulele.user.service.SysUserService;
import timing.ukulele.user.util.DecryptUtil;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static timing.ukulele.user.config.TopicConfig.EMAIL_CHANNEL;
import static timing.ukulele.user.config.TopicConfig.EMAIL_ROUTING;

/**
 * 用户
 */
@RestController
@Slf4j
@Import(BindEmailProperties.class)
public class SysUserController implements UserControllerFacade {
    private final SysUserService service;
    private final PasswordEncoder passwordEncoder;
    private final SysThirdPartyUserService thirdPartyUserService;
    private final AmqpTemplate rabbitTemplate;
    private final CacheManager cacheManager;
    private final BindEmailProperties bindEmailProperties;
    private final SysTenantFeignFacade tenantFeignFacade;

    public SysUserController(SysUserService service, PasswordEncoder passwordEncoder,
                             SysThirdPartyUserService thirdPartyUserService,
                             AmqpTemplate rabbitTemplate,
                             CacheManager cacheManager, BindEmailProperties bindEmailProperties, SysTenantFeignFacade tenantFeignFacade) {
        this.service = service;
        this.passwordEncoder = passwordEncoder;
        this.thirdPartyUserService = thirdPartyUserService;
        this.rabbitTemplate = rabbitTemplate;
        this.cacheManager = cacheManager;
        this.bindEmailProperties = bindEmailProperties;
        this.tenantFeignFacade = tenantFeignFacade;
    }

    public ResponseData<UserVO> getCurrentUser(@RequestHeader("X-USER") String currentUser) {
        SysUser user = this.service.getUserByUserName(currentUser);
        if (user != null) {
            user.setPassword(null);
            UserVO vo = new UserVO();
            BeanUtils.copyProperties(user, vo);
            return ResponseData.success(vo);
        }
        return ResponseData.success();
    }

    /**
     * 根据用户名查询用户信息
     *
     * @param username 用户名
     * @return 用户信息
     */
    @Override
    public ResponseData<UserVO> getUserByUserName(String username) {
        SysUser user = service.getUserByUserName(username);
        UserVO vo = getUserVO(user);
        return ResponseData.success(vo);
    }

    /**
     * 根据电话号码查询用户信息
     *
     * @param phone 电话号码
     * @return 用户信息
     */
    @Override
    public ResponseData<UserVO> getUserByPhone(String phone) {
        SysUser user = service.getUserByPhone(phone);
        UserVO vo = getUserVO(user);
        return ResponseData.success(vo);
    }

    /**
     * 根据电话号码或用户名查询用户
     *
     * @param phoneOrName 电话号码或用户名
     * @return 用户信息
     */
    @Override
    public ResponseData<UserVO> getUserByPhoneOrName(String phoneOrName) {
        SysUser user = service.getUserByPhoneOrName(phoneOrName);
        UserVO vo = getUserVO(user);
        return ResponseData.success(vo);
    }

    @Override
    public ResponseData<List<UserVO>> getUserByParam(Map<String, Object> map) {
        List<SysUser> userList = service.getUserByParam(map);
        List<UserVO> voList = getUserVOList(userList);
        return ResponseData.success(voList);
    }

    @Override
    public ResponseData<List<UserVO>> getByIds(Set<Long> list) {
        List<SysUser> pos = this.service.lambdaQuery().select(SysUser::getId, SysUser::getUsername, SysUser::getEmail, SysUser::getName, SysUser::getPhone, SysUser::getLabel)
                .in(SysUser::getId, list).last("limit 1000").list();
        if (CollectionUtils.isEmpty(pos))
            return ResponseData.success();
        List<UserVO> vos = new ArrayList<>(pos.size());
        pos.forEach(po -> {
            UserVO vo = new UserVO();
            BeanUtils.copyProperties(po, vo);
            vos.add(vo);
        });
        return ResponseData.success(vos);
    }

    /**
     * 根据id返回用户信息，不返回密码字段
     *
     * @param id id
     * @return 用户信息，不包含密码
     */
    @Override
    public ResponseData<UserVO> user(Long id) {
        SysUser user = this.service.getById(id);
        if (user != null) {
            user.setPassword(null);
        }
        UserVO vo = getUserVO(user);
        return ResponseData.success(vo);
    }

    /**
     * 根据id获取用户信息
     *
     * @param id id
     * @return 用户信息，包含密码字段
     */
    @Override
    public ResponseData<UserVO> userInfo(Long id) {
        SysUser user = this.service.getById(id);
        UserVO vo = getUserVO(user);
        return ResponseData.success(vo);
    }

    /**
     * 根据id删除用户，软删除
     *
     * @param id id
     * @return 是否成功
     */
    @Override
    public ResponseData<Boolean> userDel(String currentUser, Long id) {
        boolean success = this.service.lambdaUpdate().set(SysUser::getDeleted, true).set(SysUser::getUpdateBy, currentUser)
                .set(SysUser::getUpdateTime, new Date()).eq(SysUser::getId, id).update();
        return ResponseData.success(success);
    }

    /**
     * 添加用户
     *
     * @param vo 用户实体
     * @return 是否成功
     */
    @Override
    public ResponseData<Boolean> user(String currentUser, UserVO vo) {
        if (vo == null || vo.getId() != null) {
            return ResponseData.paramError();
        }
        LambdaQueryChainWrapper<SysUser> query = this.service.lambdaQuery().eq(SysUser::getUsername, vo.getUsername());
        if (StringUtils.hasText(vo.getEmail())) {
            query.or().eq(SysUser::getEmail, vo.getEmail());
        }
        if (StringUtils.hasText(vo.getPhone())) {
            query.or().eq(SysUser::getPhone, vo.getPhone());
        }
        Long exists = query.count();
        if (exists > 0) {
            return ResponseData.error(null, "该用户名或邮箱或手机号已被使用，请更换！");
        }
        SysUser user = new SysUser();
        BeanUtils.copyProperties(vo, user);
        user.setPassword(passwordEncoder.encode(StringUtils.hasLength(vo.getPassword()) ? vo.getPassword() : "123456"));
        Random random = new Random();
        int s = random.nextInt(6) % 6 + 1;
        //生成默认头像，前端资源文件中存在该路径
        user.setAvatar("assets/tmp/img/" + s + ".png");
        user.setCreateBy(currentUser);
        user.setUpdateBy(currentUser);
        boolean success = this.service.save(user);
        return ResponseData.success(success);
    }

    /**
     * 修改用户信息
     *
     * @param vo 需要修改的用户信息
     * @return 是否成功
     */
    @Override
    public ResponseData<Boolean> userUpdate(String currentUser, UserVO vo) {
        if (vo == null || vo.getId() == null) {
            return ResponseData.paramError();
        }
        // 不允许在该接口修改以下字段
        vo.setAvatar(null);
        vo.setPassword(null);
        vo.setUsername(null);
        vo.setPhone(null);
        vo.setLabel(null);
        vo.setEmail(null);
        SysUser user = new SysUser();
        BeanUtils.copyProperties(vo, user);
        user.setUpdateBy(currentUser);
        SysUser exist = this.service.getById(vo.getId());
        if (exist != null) {
            if (exist.getLabel().equalsIgnoreCase("super")) {
                if (user.getDeleted()) {
                    return ResponseData.error( "超级管理管理员不能删除");
                }
            }
        }
        boolean success = this.service.updateById(user);
        return ResponseData.success(success);
    }

    /**
     * 将数据库实体转成前端实体
     *
     * @param user 数据库用户实体
     * @return 前端用户实体
     */
    private UserVO getUserVO(SysUser user) {
        if (user != null) {
            UserVO vo = new UserVO();
            BeanUtils.copyProperties(user, vo);
            return vo;
        }
        return null;
    }

    /**
     * 将数据库用户实体列表转前端用户实体列表
     *
     * @param userList 数据库用户实体列表
     * @return 前端用户实体列表
     */
    private List<UserVO> getUserVOList(List<SysUser> userList) {
        if (!CollectionUtils.isEmpty(userList)) {
            ArrayList<UserVO> voList = new ArrayList<>(userList.size());
            userList.forEach(user -> {
                UserVO vo = getUserVO(user);
                if (vo != null)
                    voList.add(vo);
            });
            return voList;
        }
        return null;
    }

    @Override
    public ResponseData<Boolean> perfectUserInfo(UserVO vo, String username) {
        if (vo == null || !StringUtils.hasText(vo.getName()))
            return ResponseData.paramError();
        SysUser user = this.service.lambdaQuery().select(SysUser::getId).eq(SysUser::getUsername, username).one();
        this.service.lambdaUpdate().set(SysUser::getName, vo.getName()).eq(SysUser::getId, user.getId()).update();
        return ResponseData.success(Boolean.TRUE);
    }

    @Override
    public ResponseData<Map<String, Object>> getUserSecurityInfo(String username) {
        SysUser user = this.service.getUserByUserName(username);
        if (user == null)
            return ResponseData.error("用户不存在");
        Map<String, Object> result = new HashMap<>();
        result.put("email", user.getEmail());
        result.put("phone", user.getPhone());
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(6);
        boolean matches = encoder.matches("123456", user.getPassword());
        result.put("isInitPassword", matches);
        Long count = this.thirdPartyUserService.lambdaQuery().eq(SysThirdPartyUser::getUserId, user.getId()).eq(SysThirdPartyUser::getPlatSource, ThirdPartyTypeEnum.WX_APP)
                .eq(SysThirdPartyUser::getDeleted, Boolean.FALSE).count();
        result.put("minApp", count);
        return ResponseData.success(result);
    }

    @Override
    public ResponseData<String> sendEmailCode(String username, String email) {
        boolean hasCurrentUser = this.service.lambdaQuery().eq(SysUser::getUsername, username).exists();
        if (!hasCurrentUser)
            return ResponseData.error("用户不存在");
        String cacheCode = cacheManager.get(Constant.BIND_EMAIL_CHECK_CODE_PREFIX + username);
        if (cacheCode != null) {
            return ResponseData.error("验证码已经发送，请勿重复发送，请勿重复发送");
        }
        List<SysUser> list = this.service.lambdaQuery().select(SysUser::getUsername).eq(SysUser::getEmail, email).list();
        if (!CollectionUtils.isEmpty(list)) {
            // 检查是不是自己的
            long count = list.stream().filter(item -> username.equals(item.getUsername())).count();
            if (count == 0) {
                return ResponseData.error(null, "该邮箱已被其他账号绑定");
            } else {
                return ResponseData.error(null, "您已经绑定过改邮箱了，无需重复绑定");
            }
        }
        MailBean bean = new MailBean();
        bean.setSubject(bindEmailProperties.getSubject());
        bean.setToAccount(email);
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(130, 34, 6, 2);
        String code = captcha.getCode();
        cacheManager.set(Constant.BIND_EMAIL_CHECK_CODE_PREFIX + username, code, 15 * 60);
        log.info("进入缓存的重置密码验证码为：" + code);
        bean.setContent(String.format(bindEmailProperties.getContentTemplate(), code));
        rabbitTemplate.convertAndSend(EMAIL_CHANNEL, EMAIL_ROUTING, bean);
        return ResponseData.success("验证码发送成功，请至您的邮箱中查看");
    }

    @Override
    public ResponseData<String> bindEmail(String username, String email, String code) {
        SysUser user = this.service.lambdaQuery().select(SysUser::getId).eq(SysUser::getUsername, username).one();
        if (user == null)
            return ResponseData.error(null, "用户不存在");
        String cacheCode = cacheManager.get(Constant.BIND_EMAIL_CHECK_CODE_PREFIX + username);
        if (cacheCode == null) {
            return ResponseData.error(null, "验证码已过期");
        }
        if (!code.equalsIgnoreCase(cacheCode)) {
            return ResponseData.error(null, "验证码错误!");
        }
        // 检查是否已被绑定
        List<SysUser> list = this.service.lambdaQuery().select(SysUser::getUsername).eq(SysUser::getEmail, email).list();
        if (!CollectionUtils.isEmpty(list)) {
            // 检查是不是自己的
            long count = list.stream().filter(item -> username.equals(item.getUsername())).count();
            if (count == 0) {
                return ResponseData.error(null, "该邮箱已被绑定");
            } else {
                return ResponseData.error(null, "您已经绑定过改邮箱了，无需重复绑定");
            }
        }
        this.service.lambdaUpdate().set(SysUser::getEmail, email).eq(SysUser::getId, user.getId()).update();
        cacheManager.del(Constant.BIND_EMAIL_CHECK_CODE_PREFIX + username);
        return ResponseData.success("绑定成功");
    }

    public ResponseData<String> changePassword(String username, PasswordVO vo) {
        String oldPwd = vo.getOldPwd();
        String newPwd = vo.getNewPwd();
        if (!StringUtils.hasText(oldPwd) || !StringUtils.hasText(newPwd))
            return ResponseData.paramError();
        SysUser user = this.service.lambdaQuery().eq(SysUser::getUsername, username).select(SysUser::getId, SysUser::getPassword).one();
        if (null == user)
            return ResponseData.error(null, "用户不存在");
        String oldPwdDp = DecryptUtil.decryptPassword(oldPwd);
        String newPwdDp = DecryptUtil.decryptPassword(newPwd);
        BCryptPasswordEncoder bpe = new BCryptPasswordEncoder(6);
        if (bpe.matches(oldPwdDp, user.getPassword())) {
            String pwd = bpe.encode(newPwdDp);
            this.service.lambdaUpdate()
                    .set(SysUser::getPassword, pwd).eq(SysUser::getId, user.getId())
                    .set(SysUser::getUpdateTime, LocalDateTime.now())
                    .set(SysUser::getUpdateBy, username).update();
            return ResponseData.success();
        }

        return ResponseData.error("密码错误");
    }

    @Override
    public ResponseData<String> setUserDefaultTenant(String currentUser, String username, Long userId, Long tenantId) {
        if (!StringUtils.hasText(username) && userId == null) {
            return ResponseData.paramError("两个不能同时为空");
        }
        ResponseData<SysTenantVO> checkRes = this.tenantFeignFacade.checkUserTenant(username, userId, tenantId);
        if (checkRes == null || checkRes.getData() == null) {
            return ResponseData.error(null, "尚未加入该机构");
        }
        LambdaUpdateChainWrapper<SysUser> updater = this.service.lambdaUpdate().set(SysUser::getDefaultTenant, tenantId);
        if (userId != null) {
            updater.eq(SysUser::getId, userId).update();
            return ResponseData.success("成功");
        } else {
            if (StringUtils.hasText(username)) {
                updater.eq(SysUser::getUsername, updater).update();
                return ResponseData.success("成功");
            }
        }
        return ResponseData.error(null, "失败");
    }

    @GetMapping("/page/{current}/{size}")
    public ResponseData<IPage<UserVO>> getPage(@PathVariable(name = "current") int current,
                                               @PathVariable(name = "size") int size,
                                               @RequestParam(required = false, value = "username") String username,
                                               @RequestParam(required = false, value = "phone") String phone,
                                               @RequestParam(value = "label", required = false) String label,
                                               @RequestParam(value = "name", required = false) String name,
                                               @RequestParam(value = "email", required = false) String email,
                                               @RequestParam(value = "deleted", required = false) Boolean deleted) {
        if (size == 0) size = 10;
        if (current == 0) current = 1;
        IPage<UserVO> page = this.service.getPage(username, phone, label, name, email, deleted, current, size);
        if (CollectionUtils.isEmpty(page.getRecords()))
            return ResponseData.success(page);
        List<UserVO> records = page.getRecords();
        Set<Long> tenants = records.stream().map(UserVO::getDefaultTenant).collect(Collectors.toSet());
        ResponseData<List<SysTenantVO>> tenantResponse = this.tenantFeignFacade.get(tenants);
        if (tenantResponse == null || CollectionUtils.isEmpty(tenantResponse.getData()))
            return ResponseData.success(page);
        Map<Long, SysTenantVO> tenantMap = tenantResponse.getData().stream().collect(Collectors.toMap(SysTenantVO::getId, Function.identity(), (oldValue, newValue) -> newValue));
        records.forEach(item -> {
            SysTenantVO tenant = tenantMap.get(item.getDefaultTenant());
            if (tenant != null) {
                item.setDefaultTenantName(tenant.getName());
            }
        });
        return ResponseData.success(page);
    }

    @Override
    public ResponseData<List<UserVO>> batchCreateNotExist(String currentUser, List<UserVO> list) {

        // 返回值
        List<UserVO> result = new ArrayList<>();
        // 需要处理新增的数据
        List<UserVO> notExistList = new ArrayList<>();
        // 提取手机号，查询已经存在的数据
        List<String> phoneList = list.stream().map(UserVO::getPhone).collect(Collectors.toList());
        List<SysUser> existList = this.service.lambdaQuery().in(SysUser::getPhone, phoneList).list();
        if (CollectionUtil.isNotEmpty(existList)) {
            // 存在的数据直接返回
            result.addAll(BeanUtil.copyToList(existList, UserVO.class));
            // 根据存在的数据，找出需要处理新增的数据
            List<String> existPhoneList = existList.stream().map(SysUser::getPhone).toList();
            notExistList = list.stream()
                    .filter(obj -> !existPhoneList.contains(obj.getPhone()))
                    .collect(Collectors.toList());
        } else {
            notExistList = list;
        }

        if (CollectionUtil.isNotEmpty(notExistList)) {
            // 需要新增的数据
            List<SysUser> insertList = new ArrayList<>();
            SysUser user;
            for (UserVO userVO : notExistList) {
                user = new SysUser();
                BeanUtils.copyProperties(userVO, user);
                user.setDefaultTenant(null);
                // 没有则设置默认用户名
                user.setUsername(StringUtils.hasLength(userVO.getUsername()) ? userVO.getUsername() :
                        RandomUtil.randomString(12));
                // 没有则设置默认密码
                user.setPassword(passwordEncoder.encode(StringUtils.hasLength(userVO.getPassword())
                        ? userVO.getPassword() : "123456"));
                Random random = new Random();
                int s = random.nextInt(6) % 6 + 1;
                //生成默认头像，前端资源文件中存在该路径
                user.setAvatar(StringUtils.hasLength(userVO.getAvatar())
                        ? userVO.getAvatar() : "assets/tmp/img/" + s + ".png");
                user.setCreateBy(currentUser);
                user.setUpdateBy(currentUser);
                insertList.add(user);
            }
            this.service.saveBatch(insertList);
            result.addAll(BeanUtil.copyToList(insertList, UserVO.class));
        }
        // 返回数据
        return ResponseData.success(result);
    }

}
